/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Hub Name/ID Cache
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#ifndef _GIMI_NAMECACHE_HPP_
#define _GIMI_NAMECACHE_HPP_

#include <map>
#include <list>
#include <string>
#include <assert.h>
#include "sync.hpp"


namespace gimi {


  struct SNameCacheEntry
  {
    SNameCacheEntry(void)
      : name(),
        nodeid(0x00000000),
        flags(0x00000000),
        expires()
    { }
    SNameCacheEntry(const std::string &aName,
                    const uint32_t &aNodeId,
                    const gim::time &aExpires,
                    const unsigned int &aFlags = 0x00000000)
      : name(aName),
        nodeid(aNodeId),
        flags(aFlags),
        expires(aExpires)
    {
    }
    std::string name;             ///< Name of node
    uint32_t nodeid;              ///< Fully qualified nodeid
    unsigned int flags;           ///< Flags 
    gim::time expires;            ///< Expiration time
  };

  typedef std::list<SNameCacheEntry> TNameCacheEntryArray;
  typedef std::list<SNameCacheEntry>::iterator TNameCacheEntryArrayIterator;
  typedef std::list<SNameCacheEntry>::const_iterator TNameCacheEntryArrayConstIterator;
  typedef std::map<uint32_t, std::list<SNameCacheEntry>::iterator> TNameCacheEntryArrayById;
  typedef std::map<std::string, std::list<SNameCacheEntry>::iterator> TNameCacheEntryArrayByName;

  class CNameCache : public gim::CSync {
  public:
    CNameCache()
      : CSync(0,1),
        iNameCacheEntryArray(),
        iNameCacheEntryArrayById(),
        iNameCacheEntryArrayByName()
    {}
    ~CNameCache() {}
    
    bool InsertNameCacheEntry(const SNameCacheEntry &aEntry);
    inline bool GetNameCacheEntryById(SNameCacheEntry &aEntry, const uint32_t &aId) const;
    inline bool GetNameCacheEntryByName(SNameCacheEntry &aEntry, const std::string &aName) const;
    inline bool DeleteEntryById(const uint32_t &aId);
    void FlushNameCache(void);
    void ClearNameCache(void);

  private: // Functions
    inline TNameCacheEntryArrayConstIterator FindById_UL(const uint32_t &aId) const;
    inline TNameCacheEntryArrayConstIterator FindByName_UL(const std::string &aName) const;

  private: // Variables
    TNameCacheEntryArray iNameCacheEntryArray;
    TNameCacheEntryArrayById iNameCacheEntryArrayById;
    TNameCacheEntryArrayByName iNameCacheEntryArrayByName;
  };

  //***************************************************************************
  //***************************************************************************
  //***************************************************************************
  //******* Implementation ****************************************************
  //***************************************************************************
  //***************************************************************************
  //***************************************************************************
  
  inline TNameCacheEntryArrayConstIterator CNameCache::FindById_UL(const uint32_t &aId) const {
    TNameCacheEntryArrayById::const_iterator ci = iNameCacheEntryArrayById.find(aId);
    if (ci != iNameCacheEntryArrayById.end()) {
      return ci->second;

    } else {
      return iNameCacheEntryArray.end();

    }
  }
  
  inline TNameCacheEntryArrayConstIterator CNameCache::FindByName_UL(const std::string &aName) const {
    TNameCacheEntryArrayByName::const_iterator ci = iNameCacheEntryArrayByName.find(aName);
    if (ci != iNameCacheEntryArrayByName.end()) {
      return ci->second; 

    } else {
      return iNameCacheEntryArray.end();

    }
  }

  inline bool CNameCache::InsertNameCacheEntry(const SNameCacheEntry &aEntry) {
    bool result = false;
    Lock();
    
    // Look for existing entries
    TNameCacheEntryArrayConstIterator entry_byname = FindByName_UL(aEntry.name);
    TNameCacheEntryArrayConstIterator entry_byid = FindById_UL(aEntry.nodeid);
    
    // Now; clean up any existing entries (new entry always overrides)
    if (entry_byname != iNameCacheEntryArray.end()) { 
      // Found. Erase.
      dPrint(ODTEST,"Found existing match for name '%s', replacing.",
             aEntry.name.c_str());
      iNameCacheEntryArray.erase(iNameCacheEntryArrayByName.find(aEntry.name)->second);
    }
    
    if (entry_byid != iNameCacheEntryArray.end()) {
      // Found. Erase.
      dPrint(ODTEST,"Found existing match for ID '%08x', replacing.",
             aEntry.nodeid);
      iNameCacheEntryArray.erase(iNameCacheEntryArrayById.find(aEntry.nodeid)->second);
    }
    
    // Both entries now cleared.
    TNameCacheEntryArrayIterator newentry = iNameCacheEntryArray.insert(iNameCacheEntryArray.end(),
                                                                        aEntry);
    iNameCacheEntryArrayById[aEntry.nodeid] = newentry;
    iNameCacheEntryArrayByName[aEntry.name] = newentry;
    
    // Done
    result = true;

    Unlock();
    return result;
  }
  
  inline bool CNameCache::GetNameCacheEntryById(SNameCacheEntry &aEntry, const uint32_t &aId) const {
    bool result = false;
    Lock();
    TNameCacheEntryArrayConstIterator entry_byid = FindById_UL(aId);

    if (entry_byid != iNameCacheEntryArray.end()) {
      aEntry = *entry_byid;
      result = true;
      assert(aEntry.nodeid == aId);
      //      dPrint(ODTEST,"Entry Found: Got name '%s' with id '%08x'", aEntry.name.c_str(), aEntry.nodeid);
    }

    Unlock();
    return result;
  }

  inline bool CNameCache::GetNameCacheEntryByName(SNameCacheEntry &aEntry, const std::string &aName) const {
    bool result = false;
    Lock();
    TNameCacheEntryArrayConstIterator entry_byname = FindByName_UL(aName);
    
    if (entry_byname != iNameCacheEntryArray.end()) {
      aEntry = *entry_byname;
      result = true;
      assert(aEntry.name == aName);
      //      dPrint(ODTEST,"Entry Found: Got name '%s' with id '%08x'", aEntry.name.c_str(), aEntry.nodeid);
    }
    
    Unlock();
    return result;
  }
  
  inline bool CNameCache::DeleteEntryById(const uint32_t &aId) {
    bool result = false;

    Lock();
    
    TNameCacheEntryArrayById::iterator idi = iNameCacheEntryArrayById.find(aId);
    if (idi != iNameCacheEntryArrayById.end()) {
      TNameCacheEntryArrayIterator i = idi->second;
      TNameCacheEntryArrayByName::iterator ni = iNameCacheEntryArrayByName.find(i->name);
      
      // Cannot fail if the ID is present! Otherwise internal error!
      assert(ni != iNameCacheEntryArrayByName.end());
      
      // ni and idi are now the temp entries, while the i is the core storage.
      // DELETE ALL.
      iNameCacheEntryArray.erase(i);
      iNameCacheEntryArrayByName.erase(ni);
      iNameCacheEntryArrayById.erase(idi);
      result = true;
      
    }
    Unlock();

    return result;
  }
      
  inline void CNameCache::FlushNameCache(void) {
    Lock();
    dPrint(ODTEST,"Not yet implemented");
    Unlock();
  }
  
  inline void CNameCache::ClearNameCache(void) {
    Lock();
    iNameCacheEntryArray.clear();
    iNameCacheEntryArrayById.clear();
    iNameCacheEntryArrayByName.clear();
    Unlock();
  }

}
#endif
